/*
 * @Author: 0x9DEFA478
 * @Date: 2025-07-07 20:18:58
 * @LastEditTime: 2025-10-18 00:17:47
 * @LastEditors: 0x9DEFA478
 * @Description: 
 * 
 */
#include "HMakePath.hpp"
#include "Container/H_Stack.hpp"
#include "SystemUtility/C_LocalUTF8.hpp"
#include "SystemAPI/Windows/Error.hpp"
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>

#if defined(_WIN32)
#include <windows.h>
#else
#include <dirent.h>
#endif


using namespace std::literals::string_literals;


namespace HMake{

  static inline std::string SystemCPathToString(const char* CPath){
#if defined(_WIN32)
    return H::LocalSystem_To_UTF8(std::string(CPath,strnlen(CPath,FILENAME_MAX)));
#else
    return std::string(CPath,strnlen(CPath,FILENAME_MAX));
#endif
  }

#if !defined(_WIN32)
  static inline int stat(const std::string& path,struct ::stat* Stat){
    return ::stat(path.c_str(),Stat);
  }
#endif

  static inline FILE* fopen(const std::string& path,const char* Mode){
#if defined(_WIN32)
    return ::fopen(H::UTF8_To_LocalSystem(path).c_str(),Mode);
#else
    return ::fopen(path.c_str(),Mode);
#endif
  }

  static inline std::pair<int,std::string> Internal_DirectoryCreate(const std::string& path){
#if defined(_WIN32)
    if(CreateDirectoryA(H::UTF8_To_LocalSystem(path).c_str(),nullptr)==FALSE){
      return {1,SystemAPI::Windows::GetErrorString()};
    }
#else
    if(0!=mkdir(path.c_str(),0755)){
      return {1,"文件夹创建失败:"s+strerror(errno)};
    }
#endif
    return {0,""};
  }

  static std::vector<PathItem> enumFileDirectory(const H::Path& BaseDirectory,const H::Path& RelDirectory,const std::string& local=".",const std::string& parent=".."){
#if defined(_WIN32)
    std::vector<PathItem> r;
    WIN32_FIND_DATAA find_data;
    HANDLE find_handle;

    find_handle=FindFirstFileA(H::UTF8_To_LocalSystem((BaseDirectory+RelDirectory+"*").String()).c_str(),&find_data);

    if(find_handle==INVALID_HANDLE_VALUE){
      return r;
    }

    do{
      std::string name=SystemCPathToString(find_data.cFileName);

      if((name.compare(local)==0)||(name.compare(parent)==0)){
        continue;
      }

      if((find_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)==0U){
        PathItem newItem;

        newItem.path=RelDirectory+name;
        newItem.IsDirectory=false;

        r.emplace_back(newItem);
      }else{
        PathItem newItem;

        newItem.path=RelDirectory+name;
        newItem.IsDirectory=true;
        
        r.emplace_back(newItem);
      }

    }while(FindNextFileA(find_handle,&find_data));

    FindClose(find_handle);

    return r;
#else
    std::vector<PathItem> r;
    DIR* dir;
    dirent* entry;

    dir=opendir(H::UTF8_To_LocalSystem((BaseDirectory+RelDirectory).String()).c_str());

    if(dir==nullptr){
      return r;
    }

    while((entry=readdir(dir))!=nullptr){
      struct stat fileStatus;
      std::string name=SystemCPathToString(entry->d_name);

      if((name.compare(local)==0)||(name.compare(parent)==0)){
        continue;
      }

      if(0!=stat((BaseDirectory+RelDirectory+name).String(),&fileStatus)){
        continue;
      }

      if(S_ISREG(fileStatus.st_mode)){
        PathItem newItem;

        newItem.path=RelDirectory+name;
        newItem.IsDirectory=false;

        r.emplace_back(newItem);
      }else if(S_ISDIR(fileStatus.st_mode)){
        PathItem newItem;

        newItem.path=RelDirectory+name;
        newItem.IsDirectory=true;
        
        r.emplace_back(newItem);
      }

    }

    closedir(dir);

    return r;
#endif
  }

  static void enumFileDirectoryRecursive(std::list<PathItem>& dst,const H::Path& BaseDirectory,const H::Path& RelDirectory){
    struct StackItem{
      std::vector<PathItem> localPathArray;
      bool ListIsOk;
      H::Path RelDirectory;
      std::size_t Index;

      StackItem():ListIsOk(false),Index(0){}
    };

    H::Stack<StackItem> stack;
    StackItem currentStatus;

    currentStatus.RelDirectory=RelDirectory;

    for(;;){

      for(;;){
        if(!currentStatus.ListIsOk){
          currentStatus.localPathArray=enumFileDirectory(BaseDirectory,currentStatus.RelDirectory);
          currentStatus.ListIsOk=true;
        }

        std::size_t Length=currentStatus.localPathArray.size();

        if(currentStatus.Index<Length){
          auto Item=currentStatus.localPathArray[currentStatus.Index];
          currentStatus.Index++;

          if(Item.IsDirectory){
            dst.emplace_back(Item);
            stack.Push(currentStatus);
            currentStatus=StackItem();
            currentStatus.RelDirectory=Item.path;
          }
          continue;
        }

        for(auto& Item:currentStatus.localPathArray){
          if(!Item.IsDirectory){
            dst.emplace_back(Item);
          }
        }

        break;
      }

      if(stack.Empty()){
        break;
      }
      currentStatus=stack.Top();
      stack.Pop();
    }

  }

  void EnumFileDirectory(std::list<PathItem>& dst,const H::Path& BaseDirectory){
    auto localPathArray=enumFileDirectory(BaseDirectory,H::Path::Empty);
    for(auto& Item:localPathArray){
      dst.emplace_back(Item);
    }
  }

  void EnumFileDirectoryRecursive(std::list<PathItem>& dst,const H::Path& BaseDirectory){
    enumFileDirectoryRecursive(dst,BaseDirectory,H::Path::Empty);
  }

  std::pair<bool,uint64_t> FileGetLastUpdateTime(const std::string& Path){
#if defined(_WIN32)
    WIN32_FIND_DATAA find_data;
    HANDLE find_handle;
    uint64_t r;


    find_handle=FindFirstFileA(H::UTF8_To_LocalSystem(Path).c_str(),&find_data);
    if(find_handle==INVALID_HANDLE_VALUE){
      return {false,0};
    }
    FindClose(find_handle);
    r=(((uint64_t)find_data.ftLastWriteTime.dwHighDateTime)<<32)|(uint64_t)find_data.ftLastWriteTime.dwLowDateTime;
#else
    struct stat fileStatus;
    uint64_t r;

    if(0!=stat(Path,&fileStatus)){
      return {false,0};
    }
    r=fileStatus.st_mtim.tv_sec;
    r=r*1000+(fileStatus.st_mtim.tv_nsec/1000000);
#endif
    return {true,r};
  }

  std::pair<int,std::string> FileUpdateEmpty(const std::string& Path){
    auto file=fopen(Path,"w");
    if(file==nullptr){
      return {1,"文件打开失败:"s+strerror(errno)};
    }
    fclose(file);
    return {0,""};
  }

  std::pair<int,std::string> DirectoryCreate(const std::string& Path){
    H::Stack<std::string> pathStack;
    auto path=H::Path(Path);

    for(;;){
      auto pathStr=path.DiscardRelative().String();
      if(pathStr.size()<1){
        break;
      }
      auto res=FileGetLastUpdateTime(pathStr);
      if(res.first){
        break;
      }
      pathStack.Push(pathStr);
      path=(path+"../").DiscardRelative();
    }

    while(!pathStack.Empty()){
      auto res=Internal_DirectoryCreate(pathStack.Top());
      if(res.first!=0){
        auto res_mult=FileGetLastUpdateTime(pathStack.Top());
        if(!res_mult.first){//测试时应去掉这个检查, 直接认定是错误;
          return {res.first,"创建文件夹\""+pathStack.Top()+"\"时失败:"+res.second};
        }
        //res_mult.first==true 说明不是因访问冲突导致的文件夹创建失败
      }
      pathStack.Pop();
    }

    return {0,""};
  }

}

